package com.wang.ymcc.service.impl;

import com.wang.lwm.result.PageList;
import com.wang.ymcc.CourseDoc;
import com.wang.ymcc.dto.AggPageList;
import com.wang.ymcc.dto.BucketVo;
import com.wang.ymcc.dto.SearchParamDto;
import com.wang.ymcc.mapper.HighlightResultMapper;
import com.wang.ymcc.repository.CourseDocRepository;
import com.wang.ymcc.service.ICourseDocService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CourseDocServiceImpl implements ICourseDocService {
    @Autowired
    private CourseDocRepository courseDocRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;

    @Override
    public void save(CourseDoc doc) {
        courseDocRepository.save(doc);
    }

    @Override
    public void delete(Long id) {
        courseDocRepository.deleteById(id);
    }

    private String order(String sortfield) {
        switch (sortfield){
            case "xl" : return "saleCount";
            case "pl" : return "commentCount";
            case "jg" : return "price";
            case "rq" : return "viewCount";
            case "xp" : return "onlineTime";
        }
        return "saleCount";
    }

    private SortOrder ordersort(String sorttype) {
        //sorttype.toLowerCase()方法 表示该属性的值不区分大小写
        return sorttype==""||sorttype.toLowerCase()=="desc"?SortOrder.DESC:SortOrder.ASC;
    }

    @Override
    public PageList<CourseDoc> searchCourse(SearchParamDto dto) {
        //1.创建一个本机查询的builder对象  后续操作基于该对象
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //2.设置查询结果分页处理
        builder.withPageable(PageRequest.of(dto.getPage()-1, dto.getRows()));
        //3.添加排序的条件
        /*
            排序的字段 xl pl jg rq xp
            private String sortField;// 	"rq"
            排序的方式   desc
            private String sortType;// 	"desc"
            xl-->saleCount
            pl-->commentCount
            jg-->price
            rq-->viewCount
            xp-->onlineTime
         */
        //StringUtils.hasLength字符串不为null并且不为空
        if(StringUtils.hasLength(dto.getSortField())){
            //获取根据什么排序字段
            String orderName = order(dto.getSortField());
            //获取排序方式 方法ordersort();
            SortOrder sortOrder = ordersort(dto.getSortType());
            //添加排序信息
            builder.withSort(new FieldSortBuilder(orderName).order(sortOrder));
        }

        //4.添加查询规则     通过判断所有可能的查询条件判断存在哪些条件
        /*
            private String chargeName;// 	null
            private Long courseTypeId;// 	null
            private String gradeName;// 	null
            private BigDecimal priceMax;// 	null
            private BigDecimal priceMin;// 	null
         */
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if(StringUtils.hasLength(dto.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",dto.getChargeName()));
        }
        if(dto.getCourseTypeId()!=null && dto.getCourseTypeId()!=0){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",dto.getCourseTypeId()));
        }
        if (StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",dto.getGradeName()));
        }
        if (dto.getPriceMax()!=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }
        if (dto.getPriceMin()!=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        if (StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
        }
        //将过滤条件添加到java es的查询对象中
        builder.withQuery(boolQuery);

        //5.添加高亮处理
        /*
            对分词的关键字进行处理 添加标签并且添加高亮属性
         */
        HighlightBuilder.Field name = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        builder.withHighlightFields(name);

        //6.聚合处理  多条件查询字段
        //根据等级来筛选数据
        TermsAggregationBuilder gradeNameAgg = AggregationBuilders.terms("gradeNameAgg").field("gradeName");
        //根据收费规则来筛选规则
        TermsAggregationBuilder chargeNameAgg = AggregationBuilders.terms("chargeNameAgg").field("chargeName");
        //将筛选规则添加到es建设者对象中
        builder.addAggregation(gradeNameAgg).addAggregation(chargeNameAgg);

        //7.结果处理集
        //获取es的被执行数据对象
        SearchQuery query = builder.build();
        //执行查询结果  TODO 该执行对象是springboot+es的jar包
        //Page<CourseDoc> search = courseDocRepository.search(query);
        //TODO  该执行对象为es包独属执行对象 使用此方案
        AggregatedPage<CourseDoc> pageDocs = elasticsearchTemplate.queryForPage(query, CourseDoc.class,highlightResultMapper);

        //8.聚合以后的所有结果集  查询出来返回给前端
        Aggregations aggregations = pageDocs.getAggregations();
        Map<String, Aggregation> map = aggregations.asMap();
        //最终返回给前端的结果集
        HashMap<Object, Object> maplist = new HashMap<>();
        //遍历map
        map.entrySet().forEach(entry->{
            List<BucketVo> listBucketVo = new ArrayList<>();
            //转换为ParsedStringTerms方便后续获取buckets
            ParsedStringTerms value = (ParsedStringTerms) entry.getValue();
            List<? extends Terms.Bucket> buckets = value.getBuckets();
            buckets.forEach(bucket -> {
                BucketVo bucketVo = new BucketVo(bucket.getKeyAsString(), bucket.getDocCount());
                listBucketVo.add(bucketVo);
            });
            maplist.put(entry.getKey(),listBucketVo);
        });
        //pageDocs.getTotalElements(); 获取总条数
        //pageDocs.getContent();获取里面的数据的集合
        return new AggPageList(pageDocs.getTotalElements(),pageDocs.getContent(),maplist);
    }
}
